package cn.ultrarisk.scadmin.manager.biz.zookeeper.service;

import cn.ultrarisk.mybatis.extension.query.Query;
import cn.ultrarisk.mybatis.extension.query.Updater;
import cn.ultrarisk.scadmin.manager.biz.common.context.OperationType;
import cn.ultrarisk.scadmin.manager.biz.common.service.OperationLogService;
import cn.ultrarisk.scadmin.manager.biz.solr.dao.SolrCollectionDAO;
import cn.ultrarisk.scadmin.manager.biz.solr.entity.SolrCollection;
import cn.ultrarisk.scadmin.manager.biz.solr.service.SolrCollectionService;
import cn.ultrarisk.scadmin.manager.biz.zookeeper.dao.ZkNodeDAO;
import cn.ultrarisk.scadmin.manager.biz.zookeeper.entity.ZkNode;
import cn.ultrarisk.scadmin.manager.biz.zookeeper.operator.ZookeeperOperator;
import cn.ultrarisk.scadmin.manager.exception.BizCheckedException;
import cn.ultrarisk.scadmin.manager.util.CollectionUtil;
import cn.ultrarisk.scadmin.manager.util.IOUtil;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.zookeeper.data.Stat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.*;

/**
 * ZK节点数据操作类
 *
 * @author sevendlong(lin23871@163.com)
 * @date 2014/3/28
 * @since V1.0
 */
@Service
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class ZkNodeService {
    @Autowired
    private SolrCollectionService solrCollectionService;
    @Autowired
    private SolrCollectionDAO solrCollectionDAO;
    @Autowired
    private ZkNodeDAO zkNodeDAO;
    @Autowired
    private ZookeeperOperator zookeeperOperator;
    @Autowired
    private OperationLogService operationLogService;

    /**
     * @query path
     * @query backupId
     */
    public void saveByZKPath(String path, Integer backupId) {
        List<Integer> removeIds = zkNodeDAO.findColumn(Query.query(ZkNode.BACKUP_ID, backupId), ZkNode.ID, true, true);
        List<ZkNode> zkNodes = CollectionUtil.asList(zkNodeDAO.getFromZk(path, null));
        int level = 0;
        while (true) {
            List<ZkNode> levelNodes = new ArrayList<ZkNode>();
            for (ZkNode zkNode : zkNodes) {
                levelNodes.addAll(zkNode.getChildrenByLevel(level));
            }
            for (ZkNode levelNode : levelNodes) {
                levelNode.setParentId(levelNode.getParent() == null ? 0 : levelNode.getParent().getId());
                levelNode.setBackupId(backupId);
                ZkNode zkNode = zkNodeDAO.get(Query.query(ZkNode.BACKUP_ID, backupId).eq(ZkNode.PATH, levelNode.getPath()), ZkNode.ID);
                if (zkNode == null) {
                    zkNodeDAO.create(levelNode);
                } else {
                    levelNode.setId(zkNode.getId());
                    zkNodeDAO.update(levelNode);
                    removeIds.remove(zkNode.getId());
                }
            }
            if (levelNodes.size() == 0) {
                break;
            }
            ++level;
        }
        if (removeIds.size() > 0) {
            zkNodeDAO.delete(removeIds);
        }
    }

    public ZkNode create(String path, String data, Integer backupId) {
        ZkNode node = zkNodeDAO.getByPath(path, backupId);
        if (node != null) {
            return node;
        }

        String[] arr = path.split("[/|\\\\]+");
        String parentPath = CollectionUtil.join(arr, "/", 0, arr.length - 1);
        ZkNode parentNode = zkNodeDAO.getByPath(parentPath, backupId);
        if (parentNode == null) {
            throw new BizCheckedException("parent node is empty");
        }

        zookeeperOperator.create(path, data);
        Stat stat = zookeeperOperator.getStat(path);
        node = new ZkNode(stat);
        node.setPath(path);
        node.setData(data);
        node.setParentId(parentNode.getId());
        node.setBackupId(backupId);
        zkNodeDAO.create(node);
        return node;
    }

    public void updateData(Integer id, String data) {
        zkNodeDAO.update(id, Updater.update(ZkNode.DATA, data));
        ZkNode zkNode = zkNodeDAO.get(id, ZkNode.PATH);
        zookeeperOperator.putData(zkNode.getPath(), data);
        if (zkNode.getPath().endsWith("/schema.xml") || zkNode.getPath().endsWith("/solrconfig.xml")) {
            String config = zkNode.getPath().replace("/schema.xml", "").replace("/solrconfig.xml", "");
            List<SolrCollection> collections = solrCollectionDAO.findList(Query.query(SolrCollection.CONFIG, config).eq(SolrCollection.AVAILABLE, true));
            if (collections.size() > 0) {
                for (SolrCollection collection : collections) {
                    try {
                        solrCollectionService.reload(collection.getId());
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (SolrServerException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        operationLogService.createSolrConfigLog(OperationType.SOLR_CONFIG_UPDATE, id);
    }

    public void delete(Integer id, Integer backupId) {
        ZkNode node = zkNodeDAO.get(id);
        if (node == null) {
            throw new BizCheckedException("the node is empty");
        }


        List<ZkNode> subNodes = zkNodeDAO.findByLikePath(node.getPath(), backupId, false);
        subNodes.addAll(zkNodeDAO.findByLikePath(node.getPath() + "/%", backupId, false));
        Collections.sort(subNodes, new Comparator<ZkNode>() {
            @Override
            public int compare(ZkNode o1, ZkNode o2) {
                return o1.getPath().length() < o2.getPath().length() ? 1 : -1;
            }
        });

        operationLogService.createSolrConfigLog(OperationType.SOLR_CONFIG_DELETE, id);
        for (ZkNode subNode : subNodes) {
            zookeeperOperator.delete(subNode.getPath());
            zkNodeDAO.delete(subNode.getId());
        }
    }

    public void deleteByBackupId(Integer... backupIds) {
        zkNodeDAO.delete(Query.inQuery(ZkNode.BACKUP_ID, CollectionUtil.asList(backupIds)));
    }

    public void uploadFiles(String path, Collection<MultipartFile> files, Integer backupId) throws IOException {
        ZkNode node = zkNodeDAO.getByPath(path, backupId);
        if (node == null) {
            create(path, "", backupId);
        }

        List<Integer> fileNodeIds = new ArrayList<Integer>();
        for (MultipartFile file : files) {
            String filePath = (path + (path.endsWith("/") ? "" : "/") + file.getOriginalFilename()).replaceAll("[/|\\\\]+", "/");
            String fileContent = IOUtil.in2Str(new ByteArrayInputStream(file.getBytes()), "utf-8");
            ZkNode fileNode = zkNodeDAO.getByPath(filePath, backupId);
            if (fileNode == null) {
                fileNode = create(filePath, fileContent, backupId);
            } else {
                updateData(fileNode.getId(), fileContent);
            }
            fileNodeIds.add(fileNode.getId());
        }

        operationLogService.createSolrConfigLog(OperationType.SOLR_CONFIG_CREATE, fileNodeIds);
    }
}